using Cinemachine;
using Cysharp.Threading.Tasks;
using IQIGame.Onigao.Framework;
using IQIGame.Onigao.Game;
using IQIGame.Onigao.GameAOT;
using IQIGame.Rendering;
using Lean.Touch;
using System;
using System.Collections.Generic;
using UnityEngine;

namespace IQIGame.Onigao.GamePlay
{
    /// <summary>
    /// 相机控制器基类
    /// 和场景基类不同，该类包含了众多业务代码，没有和TSceneController放在一起
    /// </summary>
    public abstract class TCameraController : TModuleController
    {
        #region generate code 
        public Cinemachine.CinemachineBrain cinemachineBrain { protected set; get; }
        public Cinemachine.CinemachineVirtualCamera vc_Character { protected set; get; }
        public Cinemachine.CinemachineVirtualCamera vc_MoveTarget { protected set; get; }
        public UnityEngine.Playables.PlayableDirector tl_MoveFocus { protected set; get; }
        public UnityEngine.Transform targetMain { protected set; get; }
        public UnityEngine.Animations.PositionConstraint pos_audioListener { protected set; get; }
        public UnityEngine.Camera mainCamera { protected set; get; }
        protected virtual void InitBinding(ObjectBinding __binding)
        {
            __binding.TryGetVariableValue<Cinemachine.CinemachineBrain>("cinemachineBrain", out var __tbv0);
            this.cinemachineBrain = __tbv0;
            __binding.TryGetVariableValue<Cinemachine.CinemachineVirtualCamera>("vc_Character", out var __tbv1);
            this.vc_Character = __tbv1;
            __binding.TryGetVariableValue<Cinemachine.CinemachineVirtualCamera>("vc_MoveTarget", out var __tbv2);
            this.vc_MoveTarget = __tbv2;
            __binding.TryGetVariableValue<UnityEngine.Playables.PlayableDirector>("tl_MoveFocus", out var __tbv3);
            this.tl_MoveFocus = __tbv3;
            __binding.TryGetVariableValue<UnityEngine.Transform>("targetMain", out var __tbv4);
            this.targetMain = __tbv4;
            __binding.TryGetVariableValue<UnityEngine.Animations.PositionConstraint>("pos_audioListener", out var __tbv5);
            this.pos_audioListener = __tbv5;
            __binding.TryGetVariableValue<UnityEngine.Camera>("mainCamera", out var __tbv6);
            this.mainCamera = __tbv6;
        }
        #endregion

        public enum CameraStateType
        {
            None = 0,
            FollowCharacter,            //跟随角色
            InOperation,                //操作中
            ScreenSwitch,               //屏幕切换
        }

        /// <summary>
        /// 对象绑定
        /// </summary>
        public ObjectBinding cameraRootBinding { get; set; }

        /// <summary>
        /// 相机状态
        /// </summary>
        public CameraStateType cameraState { protected set; get; } = CameraStateType.None;

        /// <summary>
        /// 跟随主角的视角相机
        /// </summary>
        protected abstract CinemachineVirtualCamera vcCharCamera { get; }
        protected abstract CinemachinePOV vcCharCameraPov { get; }
        protected abstract CinemachineFramingTransposer vcCharCameraBody { get; }

        /// <summary>
        /// 镜头动画的虚拟相机
        /// </summary>
        protected CinemachinePOV vcMoveTargetPov;
        protected CinemachineFramingTransposer vcMoveTargetBody;

        /// <summary>
        /// 配置里获取到的相机默认跟随距离和旋转
        /// </summary>
        protected float _defaultCameraCharacterDistance;
        protected Vector2 _defaultCameraCharacterRotate;
        protected Vector2 _defaultCameraCharRotateMinMax;

        /// <summary>
        /// 常驻的相机跟随距离和旋转数据
        /// 在游戏过程中，相机本身的参数可能为了纯表现临时改变，此时下列数据不变，用于还原
        /// 但下列数据可能根据特定功能改变，还可能保存在存档里
        /// </summary>
        protected float _fixCameraCharacterDistance;
        protected Vector2 _fixCameraCharacterRotate;
        protected Vector2 _fixCameraCharRotateMinMax;

        /// <summary>
        /// 相机开始拖拽的移动阈值（像素）
        /// </summary>
        public const float DistanceThreshold = 10;
        /// <summary>
        /// 操作相机的转动速度
        /// </summary>
        public const float VcamRotateFactor = 0.2f;
        /// <summary>
        /// 拖动相机开始时，相机缓动的时长
        /// </summary>
        public const float DragTweenDuration = 0.4f;
        /// <summary>
        /// 记录不是由gui开始的finger
        /// </summary>
        protected HashSet<LeanFinger> noStartGuiFingers = new HashSet<LeanFinger>();
        protected List<LeanFinger> _tempFingers = new List<LeanFinger>();
        protected bool _isActive = false;

        #region 相机阻挡相关字段
        protected float _checkTimeOcclusion;
        protected bool _startOcclusionJudge = false;
        protected Dictionary<GameObject, Renderer[]> occlusionObjects = new Dictionary<GameObject, Renderer[]>();
        protected HashSet<GameObject> _tmpOcclusions = new HashSet<GameObject>();
        #endregion

        /// <summary>
        /// 不同控制器添加场景预加载的方法委托
        /// </summary>
        public abstract Action<Func<UniTask>> AddPreloadAfterScene { get; }
        /// <summary>
        /// 场景中玩家角色的Transform，注意该Transform并不一定是跟随或注视对象，而是用来处理3D音效源和相机碰撞的，代表的就是玩家操作的角色
        /// </summary>
        /// <returns></returns>
        protected abstract Transform GetPlayerEntityTransform();

        protected override void OnModuleGameplayInit()
        {
            AddPreloadAfterScene(PreloadCamera);
        }

        protected override void OnSceneLoaded()
        {
            // 添加3d声音接收器,位置由主角定，方向由相机定
            if (pos_audioListener != null)
            {
                pos_audioListener.gameObject.AddComponent<GameAudioListener>();
                var constraintSource = pos_audioListener.GetSource(0);
                var playerEntityTrans = GetPlayerEntityTransform();
                constraintSource.sourceTransform = playerEntityTrans ?? pos_audioListener.transform;
                pos_audioListener.SetSource(0, constraintSource);
                pos_audioListener.constraintActive = true;
            }
            _isActive = true;
        }

        protected abstract UniTask PreloadCamera();

        public virtual void CharacterFllowLook(Transform follow, Transform lookat)
        {
            this.vc_Character.Follow = follow;
            this.vc_Character.LookAt = lookat;

            cameraState = CameraStateType.FollowCharacter;
        }

        protected void CameraGesture(List<LeanFinger> fingers)
        {
            _tempFingers.Clear();
            foreach (var f in fingers)
            {
                if (!f.StartedOverGui && !f.Up)
                {
                    _tempFingers.Add(f);
                    noStartGuiFingers.Add(f);
                }
            }
            if (_tempFingers.Count == 0)
            {
                return;
            }

            //手势操作，按优先级执行，一次只能执行一种操作
            //先尝试缩放，如果成功则不继续后续逻辑
            if (OnTryPinchScaleCamera(_tempFingers))
            {
                return;
            }
            //再尝试拖拽，如果成功则不继续后续逻辑
            if (OnTryDragCamera(_tempFingers))
            {
                return;
            }
        }

        protected virtual bool OnTryPinchScaleCamera(List<LeanFinger> fingers)
        {
            return false;
        }

        protected virtual bool OnTryDragCamera(List<LeanFinger> fingers)
        {
            var delta = LeanGesture.GetScreenDelta(fingers);
            if (delta != Vector2.zero)
            {
                TrySwitchCameraToManual();
                RotateCamera(delta);
                return true;
            }
            return false;
        }

        protected void TrySwitchCameraToManual()
        {
            switch (cameraState)
            {
                case CameraStateType.FollowCharacter:
                    cameraState = CameraStateType.InOperation;
                    cinemachineBrain.m_DefaultBlend.m_Time = DragTweenDuration;
                    SwitchToManaulVirtualCamera();
                    break;
                case CameraStateType.InOperation:
                    break;
                default:
                    return;
            }
        }

        /// <summary>
        /// 切换到手动操作的虚拟相机（拖拽改变视角等）
        /// </summary>
        protected virtual void SwitchToManaulVirtualCamera()
        {

        }

        protected abstract void RotateCamera(Vector2 delta);

        protected void CameraTouchUp(LeanFinger finger)
        {
            var removed = noStartGuiFingers.Remove(finger);
            if (removed && noStartGuiFingers.Count == 0)
            {
#if UNITY_EDITOR
                if (GamePlayDebugUtil.FreeCameraOperation)
                {
                    return;
                }
#endif
                //重置相机
                ExitCameraOperation();
            }
        }

        protected void CameraTouchTap(LeanFinger finger)
        {
            if (finger.StartedOverGui)
            {
                return;
            }
            ExitCameraOperation();
        }

        protected virtual void ExitCameraOperation()
        {
            this.cameraState = CameraStateType.FollowCharacter;
        }

        public void SetPOV(CinemachinePOV pov, Vector2 value, Vector2 yAngleMinMax)
        {
            pov.m_VerticalAxis.Value = value.x;
            pov.m_HorizontalAxis.Value = value.y;
            pov.m_HorizontalAxis.m_MinValue = yAngleMinMax.x;
            pov.m_HorizontalAxis.m_MaxValue = yAngleMinMax.y;
        }

        #region 相机阻挡
        /// <summary>
        /// 设置是否开启相机阻挡判断
        /// </summary>
        /// <param name="active"></param>
        public void SetCheckCameraOcclusion(bool active)
        {
            _startOcclusionJudge = active;
            if (active)
            {
                _checkTimeOcclusion = 0f;
            }
            else
            {
                if (occlusionObjects.Count > 0)
                {
                    foreach (var kv in occlusionObjects)
                    {
                        var renders = kv.Value;
                        foreach (var r in renders)
                        {
                            r.enabled = true;
                        }
                    }
                    occlusionObjects.Clear();
                    _tmpOcclusions.Clear();
                }
            }
        }

        private void AddHitOcclusion(RaycastHit hit)
        {
            var go = hit.collider.gameObject;
            if (occlusionObjects.TryGetValue(go, out var renders))
            {
                foreach (var r in renders)
                {
                    r.enabled = false;
                }
            }
            else
            {
                renders = go.GetComponentsInChildren<Renderer>();
                occlusionObjects.Add(go, renders);
                foreach (var r in renders)
                {
                    r.enabled = false;
                }
            }
            _tmpOcclusions.Add(go);
        }

        /// <summary>
        /// 更新相机和主角之间被阻挡的情况
        /// </summary>
        public void UpdateCameraOcclusion(float deltaTime)
        {
            if (!_startOcclusionJudge)
            {
                return;
            }

            _checkTimeOcclusion += deltaTime;
            if (_checkTimeOcclusion < GameGlobalConst.EntityLogicInterval)
            {
                //_checkTime -= LevelDataConsts.EntityLogicInterval;
                return;
            }
            _checkTimeOcclusion = 0;

            _tmpOcclusions.Clear();

            // 使用射线检测判断相机和主角之间是否有遮挡物
            var cameraPosition = this.cinemachineBrain.transform.position;
            var position = GetPlayerEntityTransform().position + new Vector3(0, ViewEntityConst.CharacterHeight / 2, 0);

            int layerMask = LayerMaskConsts.Layer_Wall;

            // 从主角到相机的射线
            Vector3 directionToCamera = cameraPosition - position;

            int count = PhysicsUtility.RaycastNonAlloc10(position, directionToCamera, out var hitInfos, directionToCamera.magnitude, layerMask);
            if (count > 0)
            {
                for (int i = 0; i < count; i++)
                {
                    var hit = hitInfos[i];
                    AddHitOcclusion(hit);
                }
            }

            // 从相机到主角的射线 射线检测从相机到主角
            Vector3 directionToPlayer = position - cameraPosition;
            count = PhysicsUtility.RaycastNonAlloc10(cameraPosition, directionToPlayer, out hitInfos, directionToPlayer.magnitude, layerMask);
            if (count > 0)
            {
                for (int i = 0; i < count; i++)
                {
                    var hit = hitInfos[i];
                    AddHitOcclusion(hit);
                }
            }

            // 恢复之前被遮挡的物体
            foreach (var kv in occlusionObjects)
            {
                if (!_tmpOcclusions.Contains(kv.Key))
                {
                    var renders = kv.Value;
                    foreach (var r in renders)
                    {
                        r.enabled = true;
                    }
                }
            }
        }
        #endregion

        protected override void OnUpdate(float deltaTime)
        {
            base.OnUpdate(deltaTime);
            if (!_isActive)
            {
                return;
            }
            UpdateCameraOcclusion(deltaTime);
            UpdateRender();
        }

        private void UpdateRender()
        {
            Vector3 lookPosition;
            switch (cameraState)
            {
                case CameraStateType.ScreenSwitch:
                    lookPosition = mainCamera.transform.position + mainCamera.transform.forward * vcMoveTargetBody.m_CameraDistance;
                    break;
                default:
                    lookPosition = vcCharCamera.Follow.position;
                    break;
            }
            GameRender.Instance.SetWorldPlayerCenter(lookPosition);
        }

        public void SetCameraActive(bool value)
        {
            cameraRootBinding.gameObject.SetActive(value);
        }

        protected override void OnDispose()
        {
            _isActive = false;
        }
    }
}
